package com.weibo.basic.alg.alert.client;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import com.weibo.basic.alg.alert.client.config.NotifyConfig;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;

/**
 * @author zcauniverse@163.com
 * @version 1.0
 * @since 2019-03-08:16:11
 **/
@Slf4j
public class NotifyAppender {

    private static final String INNER_PACKAGE = "com.weibo.basic.alg.alert";
    private NotifyClient notifyClient;
    private NotifyConfig notifyConfig;
    private Thread worker;
    private LinkedBlockingQueue<ILoggingEvent> queue = new LinkedBlockingQueue<>(1024);
    private AtomicBoolean installed = new AtomicBoolean(false);
    private PatternLayoutEncoder encoder;
    private _NotifyAppender notifyAppender;

    public NotifyAppender(NotifyConfig notifyConfig) {
        this.notifyConfig = notifyConfig;
        this.notifyClient = NotifyClient.getInstance(notifyConfig.getServerUrl(), notifyConfig.isEnableDebug());
        notifyAppender = new _NotifyAppender();
        worker = new Thread(new Runnable() {
            @Override
            public void run() {

                while (!Thread.currentThread().isInterrupted()) {
                    ILoggingEvent event = null;
                    try {
                        event = queue.take();
                    } catch (InterruptedException e) {
                        log.error("interrupt", e);
                        Thread.currentThread().interrupt();
                    }
                    if (event.getLevel().isGreaterOrEqual(Level.ERROR)) {
                        notifyClient.sendMessage(new String(encoder.encode(event), StandardCharsets.UTF_8),
                            notifyConfig.getReceiveUsers(),
                            notifyConfig.getRetryTimes() <= 0 ? 1 : notifyConfig.getRetryTimes());
                    }
                }
            }
        });
        worker.setName("notifier");
        worker.setDaemon(true);
        worker.start();
    }

    public void install() {
        if (installed.compareAndSet(false, true)) {
            notifyAppender.install();
        }
    }

    private class _NotifyAppender extends AppenderBase<ILoggingEvent> {

        @Override
        protected void append(ILoggingEvent eventObject) {
            //自己内部的日志直接忽略，不然会死循环
            if (eventObject.getLevel().isGreaterOrEqual(Level.toLevel(notifyConfig.getNotifyLevel(), Level.ERROR)) &&
                !eventObject.getLoggerName().startsWith(INNER_PACKAGE)) {
                queue.offer(eventObject);
            }
        }

        private void install() {
            Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
            LoggerContext loggerContext = rootLogger.getLoggerContext();
            encoder = new PatternLayoutEncoder();
            encoder.setContext(loggerContext);
            encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss} - %msg%n");
            encoder.start();
            this.setContext(loggerContext);
            this.start();
            rootLogger.addAppender(this);
        }

    }
}
